今天我們要來談談設計模式的原則與策略,理解設計模式本質機制和它們背後的原則概念。當累積這些知識後,即使在未來寫程式時,還沒有發現設計模式的情況下,也能夠知道該怎麼做。
以下是今天會談到的內容:
OCP (Open-Closed Principle),模組、方法和類別應該對擴展開放,對修改封閉。也就是說,應該將軟體設計成不對其修改就能擴展功能。
在昨天的Abstract Factory模式也有提到,如果利用 switch
或 if else
去做判斷回傳實例,那每增加一種新的類別,就要去方法裡多寫一行判斷去回傳新實例,這就很明顯的違反開放封閉原則。
但要完全遵守開放封閉原則幾乎是不太可能,就算是已經抽象到不能再抽,還是可能會因為「特殊化」的需求而違反,不過我們還是可以把它當作是一個目標,去遵循一個正確的方向。
在設計各部分所呈現的細節之前先建立一個整體概念,以下是從背景設計的流程:
步驟 | 討論 |
---|---|
找出模式 | 找到問題中存在的模式,用這些模式來思考問題。請記住,模式的用途是定義實體之間的關係 |
從背景模式開始 | 找出為其他模式創造了背景的模式。這些模式應該作為設計的起點 |
然後,從背景轉向內部 | 觀察其餘的模式和任何其他可能已經發現的模式,從中選出為其餘模式定義背景的模式。重複這一過程 |
改進設計 | 觀察其餘的模式和任何其他可能已經發現的模式,從中選出為其餘模式定義背景的模式。重複這一過程 |
實作 | 實作應該融入模式所要求的細節 |
建築師 Alexander認為,優秀的設計師應該遵循的規則
我們先回顧一下Bridge模式
(圖片來源:https://www.bogotobogo.com/DesignPatterns/images/bridge/bridgediagram.png)
在 Bridge模式中,Abstraction在抽象層次定義了服務,而在實作開發介面時,也考慮到了具體抽象中所說明的概念的需求,而不僅僅是面對特殊情況,這就是所謂「相依倒置原則」(Dependency Inversion Principle,DIP),DIP的原則如下:
Alexander 將此稱為「複雜化」,一種從最簡單(概念性)的層次開始,然後逐漸增加細節和特徵,隨著逐步深化,設計也漸趨複雜的過程。複雜化和相依倒置是使用設計模式的中心基礎原則,這一原則隱含著使用物件和被使用物件之間只能在概念層次存在耦合,而非實作層次。
如果有看完前面介紹過的模式,可以發現到我的所有範例中都有一個相似之處:『繼承層次中類別很少超過兩層』。那些層次更多的設計往往都是因為有設計模式的結構要求,有兩層作為衍生類別的基礎(明天介紹到的Decorator模式就是一個使用了三層的例子)。
如果出現了多層繼承,幾乎總是為了要消除某種冗餘。之所以如此,是因為設計目標之一就是不讓一個類別封裝兩個要變化的事物,除非這些變化明確地耦合在一起。如此會降低內聚性,變化之間的耦合也無法鬆散。
Bridge模式中的實作除了能夠透過一個公共介面存取之外完全不同。新的實作部分可以透過在此介面中實作相關功能系統加入。因此,模式並非只能封裝變化,還有助於找到物件之間的關係,幫助我們思考問題領域中的關鍵概念。
抽象類別和介面之間的一個差別,就是抽象類別允許有公共的狀能和行為,也就是說,如果所有衍生類別都有一些公共的狀態或者行為,就可以放在抽象類別中,對於Java 和C#來說,只允許從一個類別繼承。換言之,在不需要的時候不要使用抽象類別,因為只有從一個類別衍生的機會。
因此,具有公共狀態或行為的物件從這個抽象類別衍生,而不直接共用這一狀態或者行為的物件(或者由於其他原因必須從另一個類別衍生的物件)實作介面。
我們算一算前面也介紹過六種設計模式了,但這些模式的概念其實不是真理,他們只是真理的抽象,我們真正在做專案寫程式時,還是需要依照真實世界的具體問題去做分析,以下是在使用模式時常見的錯誤,也是我們應該避免的:
錯誤 | 產生條件 |
---|---|
浮於表面 | 僅僅對低層情況有了一些膚淺的理解,就草草選擇一個模式 |
偏見 | 對模式過於偏信。根據已經選定的模式/模型來解釋所有資料,不願意對自己的偏見有任何質疑 |
錯選 | 不理解模式適用的背景和條件(對各模式的分類關係理解不全),選擇了錯誤的模式 |
誤判 | 不熟悉各種模式,因為無知而導致誤判 |
削足適履 | 忽略了實際的、具體實體行為中的例外情況,因為它們似乎不符合模式中所表達的理論。很可能會使所建模出來的物件過於僵硬,不符合實際情況 |
今天先寫到這裡,明天我們繼續介紹其他模式,會是「Decorator模式」,那就明天見囉~